////////////////////////////////////////////////////////////////////////////////
//
//	Original file Name: Level.java
//	Originating Author: Jon Whitefield
//	
//	Description: Represents a single level 
//
////////////////////////////////////////////////////////////////////////////////

package com.jjw.game.world.level;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import com.jjw.game.world.entity.character.Entity;
import com.jjw.game.world.tile.Tile;
import com.jjw.game.world.tile.TileManager;

/**
 * Represents a single level
 * 
 * @author Jon Whitefield
 */
public class Level {
	
	//--------------------------------------------------------------------------
	//
	//Properties
	//
	//--------------------------------------------------------------------------
	
	//----------------------------------
	// Public
	//----------------------------------
	
	/**
	 * The marker which separates the tiles string into single tiles 
	 */
	private static final String LEVEL_FILE_TILE_SEPERATOR = ","; 
	
	//----------------------------------
	//  Getters / Setters
	//----------------------------------
	
	/** 
	 * Return the level reference
	 */
	public String getLevelReference() {
		return this.levelReference;
	}
	
	/**
	 * Gets the map for the level
	 */
	public Tile[][] getMap() {
		return map;
	}

	protected void setMap(Tile[][] map) {
		this.map = map;
	}
	
	/**
	 * The entity's which are in the level 
	 */
	public List<Entity> getLevelEntityList() {
		return this.levelEntityList;
	}

	protected void setLevelEntityList(List<Entity> levelEntityList) {
		this.levelEntityList = levelEntityList;
	}
	
	/**
	 * Returns if the level is loaded or not
	 */
	boolean getIsLoaded() {
		
		//Return if the level is loaded
		return this.isLoaded;
	}
	
	//----------------------------------
	// Private
	//----------------------------------
	
	/**
	 * Reference for the level 
	 */
	private String levelReference;
	
	/**
	 * Level file for the level
	 */
	private File levelFile;
	
	/**
	 * List of the tile id's which make up the tile background for the level 
	 */
	private Tile[][] map;
	
	/**
	 * The entity's which are in the level 
	 */
	private List<Entity> levelEntityList = null;

	/**
	 * Is the level loaded flag
	 */
	private boolean isLoaded = false;
	
	//--------------------------------------------------------------------------
	//
	//  Methods
	//
	//--------------------------------------------------------------------------
	
	//----------------------------------
	// Public
	//----------------------------------
		
	Level(String levelReference, File levelFile ) {
	
		//Set the level reference
		this.levelReference = levelReference;
		
		//Set the level file
		this.levelFile = levelFile;
		
		//Initialise the entity list
		this.levelEntityList = new ArrayList<Entity>();
	}

	/**
	 * Loads the level file variables into this level class
	 */
	public void loadLevel() {
		
		//Create a new empty map to destroy the old one if one exists
		this.map = new Tile[1][1];
		
		//Set the is loaded flag to false
		this.isLoaded = false;
		
		//No level file no loading of map
		if( this.levelFile == null )
			return;
		
		//If the level file dosn't exist then stop here
		if( !levelFile.exists() )
			return;
		
		//Will contain a row of the tile map
		List<String> loadedTileMapLine = new ArrayList<String>();
		
		//The width of the tile map 
		int tileMapWidth = 0;
		
		//Try reading the file
		try {
			
			//Scanner used to read the level file
			Scanner fileReader = new Scanner( levelFile );
			
			//Represents a line in the level
			String levelLine = "";
					
			//Loop through the lines in the file
			while( fileReader.hasNextLine() ) {
				
				//Get a line from the level file, trimmed
				levelLine = fileReader.nextLine().trim();
				
				//If the line is empty then continue on
				if( levelLine.equals("") )
					continue;
			
				//Set the tile map width with the bigger value
				tileMapWidth = Math.max( tileMapWidth , levelLine.split(LEVEL_FILE_TILE_SEPERATOR).length);
				
				//Add this line to the level line list 
				loadedTileMapLine.add( levelLine );
			}
		} catch (FileNotFoundException e) {
			
			//Failed to read the level file
			e.printStackTrace();
		}
						
		//Create a new loaded tile map
		Tile[][] loadedTileMap = new Tile[ loadedTileMapLine.size() ][ tileMapWidth ];
		
		//Loop through each of the lines 
		for( int i = 0; i < loadedTileMapLine.size(); i++ ) {
			
			//Split the tile row to get each tile
			String[] splitTileRow = loadedTileMapLine.get(i).split(LEVEL_FILE_TILE_SEPERATOR); 
			
			int lastIndex = 0;
			
			//Loop through the the tiles adding them to the map
			for( int k = 0; k < splitTileRow.length; k++ ) {
				
				//Set the tile 
				loadedTileMap[ i ][ k ] = TileManager.createTile( splitTileRow[k] );
				
				//Set the last index
				lastIndex = k;
			}
			
			//While we havn't got to the end of the map 
			while( lastIndex < loadedTileMap[0].length - 1 )
			{
				//Up the index
				lastIndex++;
				
				//Set the rest of the row to empty if we didn't get to the end of the list
				loadedTileMap[ i ][ lastIndex ] = null;
			}
		}
		
		//Set the tile map
		this.setMap(loadedTileMap);
		
		//Set the loaded flag to true
		this.isLoaded = true;
	}
	
	/**
	 * Makes the entity part of the level until its unregisters or the level ceases to exist
	 */
	public void registerEntity( Entity toRegister ) {
		
		//We can't add it to a null list
		if( this.levelEntityList == null)
			return;
		
		//If the entity is already on the list don't add it again 
		if( this.levelEntityList.contains(toRegister) )
			return;
		
		//Add the entity to the level
		this.levelEntityList.add(toRegister);
	}

	/**
	 * Removes the entity from the level 
	 */
	public void unregisterEntity( Entity toRemove ) {
		
		//We can't remove it if its a null list
		if( this.levelEntityList == null)
			return;
		
		//If the entity is already we want to remove it 
		if( this.levelEntityList.contains(toRemove) )
			this.levelEntityList.remove(toRemove);
	}
	
	/**
	 * Gets the width of the current level 
	 * 
	 * @return
	 */
	public int levelWidth(){
		
		//If there is no map then return zero 
		if( this.map == null)
			return 0;
		
		//If the map has no width then return zero
		if( this.map.length == 0 )
			return 0;
		
		//Times the tile size by the map width 
		return Tile.DEFAULT_TILE_SIZE * this.map[0].length;
	}
	
	public int levelHeight(){
		
		//If there is no map then return zero 
		if( this.map == null)
			return 0;
		
		//Times the tile size by the map height 
		return Tile.DEFAULT_TILE_SIZE * this.map.length;
	}
	
	//----------------------------------
	// Private
	//----------------------------------
	

	//----------------------------------
	// Overridden / Implemented
	//----------------------------------	
}